<!DOCTYPE html>
<html class="no-js" lang="zh">
  <head>
<meta charset="utf-8">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<link rel="alternate" href="https://www.desgard.com" hreflang="pt-BR">
<link href="http://gmpg.org/xfn/11" rel="profile">
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
<meta name="theme-color" content="#005344">
<title>Guardia · 瓜地</title>
<meta name="google-site-verification" content="zS1dSn20XtA4FJYEOQLXqI0boxZdMnJ2g3beje-cl20">
<meta name="description" content="I write many code to write less code.💻">
<meta name="keywords" content="">
<!-- Social: Facebook / Open Graph -->
<meta property="og:url" content="https://www.desgard.com/fishhook-2/">
<meta property="og:title" content="       验证试验 - 探求 fishhook 原理（二） | Gua  ">
<meta property="og:description" content="I write many code to write less code.💻">
<meta property="og:site_name" content="Desgard_Duan">
<meta property="og:locale" content="pt_BR">
<meta property="og:type" content="website">
<meta property="og:author" content="https://www.facebook.com/desgard.duan">
<meta property="og:image" content="https://www.desgard.com">
<!-- Social: Twitter -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@nandomoreirame">
<meta name="twitter:domain" content="https://www.desgard.com">
<meta name="twitter:title" content="       验证试验 - 探求 fishhook 原理（二） | Gua  ">
<meta name="twitter:description" content="I write many code to write less code.💻">
<meta name="twitter:image:src" content="https://www.desgard.com">
<!-- Favicons -->
<link rel="apple-touch-icon" sizes="114x114" href="https://www.desgard.com/assets/ico/apple-touch-icon-114-516f4e19976b9e4dbb77ad9b576831fe.png">
<link rel="apple-touch-icon" sizes="72x72" href="https://www.desgard.com/assets/ico/apple-touch-icon-72-5409b2df229305703caf583d86c845ab.png">
<link rel="apple-touch-icon" href="https://www.desgard.com/assets/ico/apple-touch-icon-57-aa873e019cf659e0d4e6a0b5bb9f379d.png">
<link rel="shortcut icon" href="https://www.desgard.com/assets/ico/favicon-4298be3d3fbe23e18d65bded9d930899.png">
<!-- rel prev and next -->
<!-- Canonical link tag -->
<link rel="canonical" href="https://www.desgard.com/fishhook-2/">
<link rel="alternate" type="application/rss+xml" title="Gua" href="https://www.desgard.com/feed.xml">
<link rel="stylesheet" href="/assets/main-0b7b828712f4c43b75bba4861c907bb1.css">
<script src="/assets/modernizr-custom-cb807611a3e262b2eac59444cbab74d6.js" data-cfasync="false"></script>
<script src="/assets/js/jquery-3.1.js"></script>
<script src="/assets/js/jquery.rotate.js"></script>
<script type="text/javascript" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script type="application/ld+json">
{
  "@context": "http://schema.org",
  "@type": "Website",
  "publisher": "www.desgard.com",
  "url": "http://www.desgard.com/",
  "description": "瓜地"
}
</script>
<script type="text/javascript">
  var disqus_shortname = 'desgard',
      baseurl          = '';
</script>
<!--
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-52446115-1']);
_gaq.push(['_trackPageview']);
(function() {
  var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
  ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
  var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>-->
<script>
var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "//hm.baidu.com/hm.js?c5a8123bc51782a3083a631ed9ff50e4";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();
</script>
<script type="text/x-mathjax-config">
    MathJax.Hub.Config({
      tex2jax: {
        inlineMath: [ ['$','$'] ],
        displayMath: [ ['$$','$$'] ],
        processEscapes: true
      }
    });
  </script>
  </head>
  <body class="post-template single">
    <header class="header">
  <div class="container">
    <h1><a href="/"><strong>desgard</strong>.com</a></h1>
    <nav class="navbar">
      <ul>
        <li><a href="/">Home</a></li>
        <li><a href="#!" data-modal="modalSearch"><i class="fa fa-search"></i></a></li>
        <li><a href="/comment " target="_blank"><i class="fa fa-comments"></i></a></li>
        <li><a href="/feed.xml" target="_blank"><i class="fa fa-feed"></i></a></li>
      </ul>
    </nav>
  </div>
</header>
    <main class="wrapper container" itemprop="mainContentOfPage" itemscope="itemscope" itemtype="http://schema.org/Blog">
      <article class="post" itemscope="itemscope" itemtype="http://schema.org/BlogPosting" itemprop="blogPost">
  <header class="post-header">
    <h1>验证试验 - 探求 fishhook 原理（二）</h1>
    <div class="post-meta">
      <time datetime="2015-10-13">
        <i class="fa fa-calendar-o"></i> <time datetime="2018-02-04"> 2018-02-04
      </time>
      <span>
        <i class="fa fa-comments"></i> <a href="#container">Comment me!</a>
      </span>
      <span>
<!--
        <i class="fa fa-folder-open-o"></i> 
-->
      </span>
    </div>
  </header>
  <h2 id="demo-code">示例 Demo Code</h2>
<p>继续使用上一篇文中的代码示例：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1">#include &lt;stdio.h&gt;</span>
<span class="c1">#include &quot;fishhook.h&quot;</span>
<span class="n">static</span> <span class="n">int</span> <span class="p">(</span><span class="o">*</span><span class="n">original_strlen</span><span class="p">)(</span><span class="n">const</span> <span class="n">char</span> <span class="o">*</span><span class="n">_s</span><span class="p">);</span>
<span class="n">int</span> <span class="n">new_strlen</span><span class="p">(</span><span class="n">const</span> <span class="n">char</span> <span class="o">*</span><span class="n">_s</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="mi">666</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">int</span> <span class="n">main</span><span class="p">(</span><span class="n">int</span> <span class="n">argc</span><span class="p">,</span> <span class="n">const</span> <span class="n">char</span> <span class="o">*</span> <span class="n">argv</span><span class="o">[]</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">struct</span> <span class="n">rebinding</span> <span class="n">strlen_rebinding</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">&quot;strlen&quot;</span><span class="p">,</span> <span class="n">new_strlen</span><span class="p">,</span>
        <span class="p">(</span><span class="n">void</span> <span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="n">original_strlen</span> <span class="p">};</span>
    <span class="n">rebind_symbols</span><span class="p">((</span><span class="n">struct</span> <span class="n">rebinding</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span><span class="p">){</span> <span class="n">strlen_rebinding</span> <span class="p">},</span> <span class="mi">1</span><span class="p">);</span>
    <span class="n">char</span> <span class="o">*</span><span class="n">str</span> <span class="o">=</span> <span class="s2">&quot;hellolazy&quot;</span><span class="p">;</span>
    <span class="nb">printf</span><span class="p">(</span><span class="s2">&quot;%d</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">,</span> <span class="n">strlen</span><span class="p">(</span><span class="n">str</span><span class="p">));</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<p>运行之后将其生成的执行文件拖入 <em>MachOView</em> 中。对 Mach-O 进行解析。</p>
<h2 id="linkedit-base-address-">Linkedit Base Address 计算</h2>
<p>从 <em>MachOView</em> 中 <em>Load Commands -&gt; LC_SEGMENT_64(__LINKEDIT)</em> 中获取到以下值：</p>
<p><img src="../assets/images/blog/15142784594980/15156323965359.jpg" alt="" /></p>
<p>进行下列<em>式(1)</em>和<em>式(2)</em>的计算：</p>
<script type="math/tex; mode=display">\begin{equation}
\left\{
\begin{array}{l@{\;=\;}l}
vmaddr=1000030000_{(hex)}\\
file\ offset=30000_{(hex)}
\end{array}
\right.
\end{equation}</script>
<script type="math/tex; mode=display">% <![CDATA[
\begin{equation}
\begin{aligned}
base\_address&=slide+vmaddr-file\ offset \\
&=slide+1000000000_{(hex)}
\end{aligned}
\end{equation} %]]></script>
<h2 id="section">符号表、字符表和跳转表</h2>
<p>继续看上文的这张图片：</p>
<p><img src="../assets/images/blog/15142784594980/15156340210958.jpg" alt="" /></p>
<p>此时，我们已经获取到了 <em>Base Address</em>，根据之前的流程，需要找到 <em>__LINKEDIT Section</em> 中的 Symbols 表、Indirect Symbols 表和 String 字符串表，这些地址我们需要在 Load Commands 中来获取。找到 <em>LC_SYMTAB</em> 和 <em>LC_DYSYMTAB</em> 这两个 Commaneds。</p>
<p><img src="../assets/images/blog/15142784594980/15177132918950.jpg" alt="" /></p>
<p><img src="../assets/images/blog/15142784594980/15177133141461.jpg" alt="" /></p>
<script type="math/tex; mode=display">\begin{equation}
\left\{
\begin{array}{l@{\;=\;}l}
symbol\_offset=symtab\_cmd\to symoff=12680_{(oct)}=3188_{(hex)}\\
indirect\_symbol\_offset=dysymtab\_cmd\to symoff =14008_{(oct)}=35B8_{(hex)}\\
string\_table\_offset=symtab\_cmd\to stringoff=13864_{(oct)}=3628_{(hex)}
\end{array}
\right.
\end{equation}</script>
<script type="math/tex; mode=display">\begin{equation}
\left\{
\begin{array}{l@{\;=\;}l}
symbol\_base=base\_address+symbol\_offset=slide+100003188_{(hex)}\\
indirect\_symbol\_base=base\_address+indirect\_symbol\_offset=slide+1000035B8_{(hex)}\\
string\_table\_base=base\_address+string\_table\_offset=slide+100003628_{(hex)}
\end{array}
\right.
\end{equation}</script>
<p><img src="../assets/images/blog/15142784594980/15177134982501.jpg" alt="" /></p>
<p><img src="../assets/images/blog/15142784594980/15177135190950.jpg" alt="" /></p>
<p><img src="../assets/images/blog/15142784594980/15177135318635.jpg" alt="" /></p>
<h2 id="nl--la-">跳转表 nl 和 la 绑定符号基址</h2>
<p>我们在 MachOView 中验证了三个表的位置。之后继续跟随着 fishhook 的思路来进行验证。下面将进入二尺遍历 Load Command 流程。这一次需要拿出的 Command 是 <em>LC_SEGMENT(__DATA)</em>，目的是为了找到 <code>__nl_symbol_ptr</code> 和 <code>__la_symbol_ptr</code> 这两个 Section 的位置。</p>
<script type="math/tex; mode=display">\begin{equation}
\left\{
\begin{array}{l@{\;=\;}l}
nl\_indirect\_sym\_index=13_{(oct)}\\
la\_indirect\_sym\_index=15_{(oct)}\\
\end{array}
\right.
\end{equation}</script>
<script type="math/tex; mode=display">% <![CDATA[
\begin{equation}
\left\{
\begin{array}{l@{\;=\;}l}
nl\_sym\_base\_addr&=nl\_indirect\_sym\_index\times size+indirect\_symbol\_base\\
&=13_{(oct)}\times 4 + (1000035B8_{(hex)}+slide)=1000035EC_{(hex)}+C\\
la\_sym\_base\_addr&=la\_indirect\_sym\_index\times size+indirect\_symbol\_base\\
&=15_{(oct)}\times 4 + (1000035B8_{(hex)}+slide)=1000035F4_{(hex)}+C\\
\end{array}
\right.
\end{equation} %]]></script>
<p>这里的 C 其实是上方 slide 常量的运算结果，由于在这个场景下 MachOView 验证时发现 <code>slide = 0</code>，但不意味着 <code>slide</code> 是一个恒为 0 的值。<code>vmaddr_slide</code> 的取值其实是<strong>地址空间布局随机化（ASLR）</strong>机制的结果，这是一种针对缓冲区溢出的安全保护技术，这里不再赘述。</p>
<p><img src="../assets/images/blog/15142784594980/15177136852076.jpg" alt="" /></p>
<p>根据 <em>Indirect Symbols</em> 中的描述，发现下标标识的区域的起始位置与我们的计算相吻合，此处也再次验证成功。在 <em>Indirect Symbols</em> 的结构中，我们可以找到其 <em>Lazy Symbol Pointer</em>。例如，我们在代码中出现的 <code>strlen</code> 方法：</p>
<p><img src="../assets/images/blog/15142784594980/15177139138121.jpg" alt="" /></p>
<p><img src="../assets/images/blog/15142784594980/15177139350311.jpg" alt="" /></p>
<h2 id="section-1">在符号表中获取全部信息</h2>
<p>在 <code>Lazy Symbol Pointers</code> 这里，我们最终获取到了 <code>0x10002068 -&gt; _strlen</code> 这个值。它起始是对于当前 <code>_strlen</code> 方法在符号表中的一个映射，我们可以简单的理解为<strong>它就是在符号表中对应方法的下标</strong>。</p>
<p>这个结论虽然至今笔者无法跟踪代码，将其结构实例化，但是可以通过 fishhook 的源码进行分析得出结论。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o">.</span><span class="n">.</span><span class="o">.</span>
<span class="sr">//</span> <span class="err">这里直接将</span> <span class="no">Lazy</span> <span class="no">Symbol</span> <span class="no">Point</span> <span class="err">的一个内容强转为二阶指针</span>
<span class="sr">//</span> <span class="err">也就是说明每个</span> <span class="no">Lazy</span> <span class="no">Symbol</span> <span class="no">Point</span> <span class="err">也是一个指针（当然从命名中也能猜出）</span>
<span class="n">void</span> <span class="o">**</span><span class="n">indirect_symbol_bindings</span> <span class="o">=</span> <span class="p">(</span><span class="n">void</span> <span class="o">**</span><span class="p">)((</span><span class="n">uintptr_t</span><span class="p">)</span><span class="n">slide</span> <span class="o">+</span> <span class="n">section</span><span class="o">-&gt;</span><span class="n">addr</span><span class="p">);</span>
<span class="o">.</span><span class="n">.</span><span class="o">.</span>
<span class="sr">//</span> <span class="err">在遍历每一个内容的时候，先拿出其对应的</span> <span class="n">index</span>
<span class="n">uint32_t</span> <span class="n">symtab_index</span> <span class="o">=</span> <span class="n">indirect_symbol_indices</span><span class="o">[</span><span class="n">i</span><span class="o">]</span><span class="p">;</span>
<span class="sr">//</span> <span class="err">之后直接从符号表中用下标访问的方法来获取信息</span>
<span class="n">uint32_t</span> <span class="n">strtab_offset</span> <span class="o">=</span> <span class="n">symtab</span><span class="o">[</span><span class="n">symtab_index</span><span class="o">].</span><span class="n">n_un</span><span class="o">.</span><span class="n">n_strx</span><span class="p">;</span></code></pre></div>
<p><code>symtab[symtab_index].n_un.n_strx</code> 从这个获取的方式来看，<code>symtab_index</code> 处的位置对应的是某个符号表的基址位，因此可以将类型转成 <code>nlist_64</code>。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">struct</span> <span class="n">nlist_64</span> <span class="p">{</span>
    <span class="n">union</span> <span class="p">{</span>
        <span class="n">uint32_t</span>  <span class="n">n_strx</span><span class="p">;</span> <span class="sr">/* 符号表中的位置 */</span>
    <span class="p">}</span> <span class="n">n_un</span><span class="p">;</span>
    <span class="n">uint8_t</span> <span class="n">n_type</span><span class="p">;</span>        <span class="sr">/* 经过掩码处理，表示重定义符号不同的种类 */</span>
    <span class="n">uint8_t</span> <span class="n">n_sect</span><span class="p">;</span>        <span class="sr">/* section 的编号 */</span>
    <span class="n">uint16_t</span> <span class="n">n_desc</span><span class="p">;</span>       <span class="sr">/* 类似于 n_type 经过掩码处理，用来定义一些性质 */</span>
    <span class="n">uint64_t</span> <span class="n">n_value</span><span class="p">;</span>      <span class="sr">/* 记录信息的特殊值 */</span>
<span class="p">};</span></code></pre></div>
<p>为什么在 fishhook 源码中可以将符号名称直接通过 <code>char *symbol_name = strtab + strtab_offset;</code> 直接取得呢？因为在 String Table 存储过程中会在每一个符号名的末尾增加不确定个 <code>\0</code>，而在 C 中一次的字符数组获取会以 <code>\0</code> 为结束。在 MachOView 中已经将符号名进行了解析，我们可以清晰的在解析后的 <code>nlist</code> 结构中发现符号名：</p>
<p><img src="../assets/images/blog/15142784594980/15177180501231.jpg" alt="" /></p>
<p>此时我们已经找到了改符号表的位置，剩下的工作仅需将 <code>Lazy Symbol Pointers</code> 中对应的指针进行指向变更即可完成操作。我在 fishhook 中加入了部分输出验证代码，可以大致验证这一地址的更变，但是由于在运行时有着 ASLR 的参与，是无法精准的将地址完全与 MachOView 的解析结果精准比对，但是从地址的大致区域中可以看出这个操作的成功性，以下是加入代码和输出的 log：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">static</span> <span class="n">void</span> <span class="n">perform_rebinding_with_section</span><span class="p">(</span><span class="n">struct</span> <span class="n">rebindings_entry</span> <span class="o">*</span><span class="n">rebindings</span><span class="p">,</span>
                                           <span class="n">section_t</span> <span class="o">*</span><span class="n">section</span><span class="p">,</span>
                                           <span class="n">intptr_t</span> <span class="n">slide</span><span class="p">,</span>
                                           <span class="n">nlist_t</span> <span class="o">*</span><span class="n">symtab</span><span class="p">,</span>
                                           <span class="n">char</span> <span class="o">*</span><span class="n">strtab</span><span class="p">,</span>
                                           <span class="n">uint32_t</span> <span class="o">*</span><span class="n">indirect_symtab</span><span class="p">)</span> <span class="p">{</span>
    <span class="sr">//</span> <span class="err">在</span> <span class="no">Indirect</span> <span class="no">Symbol</span> <span class="err">表中检索到对应位置</span>
    <span class="n">uint32_t</span> <span class="o">*</span><span class="n">indirect_symbol_indices</span> <span class="o">=</span> <span class="n">indirect_symtab</span> <span class="o">+</span> <span class="n">section</span><span class="o">-&gt;</span><span class="n">reserved1</span><span class="p">;</span>
    <span class="sr">//</span> <span class="err">获取</span> <span class="n">_DATA</span><span class="o">.</span><span class="n">__nl_symbol_ptr</span><span class="p">(</span><span class="err">或</span><span class="n">__la_symbol_ptr</span><span class="p">)</span> <span class="no">Section</span>
    <span class="sr">//</span> <span class="err">已知其</span> <span class="n">value</span> <span class="err">是一个指针类型，整段区域用二阶指针来获取</span>
    <span class="n">void</span> <span class="o">**</span><span class="n">indirect_symbol_bindings</span> <span class="o">=</span> <span class="p">(</span><span class="n">void</span> <span class="o">**</span><span class="p">)((</span><span class="n">uintptr_t</span><span class="p">)</span><span class="n">slide</span> <span class="o">+</span> <span class="n">section</span><span class="o">-&gt;</span><span class="n">addr</span><span class="p">);</span>
    <span class="sr">//</span> <span class="err">用</span> <span class="n">size</span> <span class="o">/</span> <span class="err">一阶指针来计算个数，遍历整个</span> <span class="no">Section</span>
    <span class="k">for</span> <span class="p">(</span><span class="n">uint</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">section</span><span class="o">-&gt;</span><span class="n">size</span> <span class="o">/</span> <span class="n">sizeof</span><span class="p">(</span><span class="n">void</span> <span class="o">*</span><span class="p">);</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="sr">//</span> <span class="err">通过下标来获取每一个</span> <span class="no">Indirect</span> <span class="no">Address</span> <span class="err">的</span> <span class="no">Value</span>
        <span class="sr">//</span> <span class="err">这个</span> <span class="no">Value</span> <span class="err">也是外层寻址时需要的下标</span>
        <span class="n">uint32_t</span> <span class="n">symtab_index</span> <span class="o">=</span> <span class="n">indirect_symbol_indices</span><span class="o">[</span><span class="n">i</span><span class="o">]</span><span class="p">;</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">symtab_index</span> <span class="o">==</span> <span class="no">INDIRECT_SYMBOL_ABS</span> <span class="o">||</span> <span class="n">symtab_index</span> <span class="o">==</span> <span class="no">INDIRECT_SYMBOL_LOCAL</span> <span class="o">||</span>
            <span class="n">symtab_index</span> <span class="o">==</span> <span class="p">(</span><span class="no">INDIRECT_SYMBOL_LOCAL</span>   <span class="o">|</span> <span class="no">INDIRECT_SYMBOL_ABS</span><span class="p">))</span> <span class="p">{</span>
            <span class="n">continue</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="sr">//</span> <span class="err">获取符号名在字符表中的偏移地址</span>
        <span class="n">uint32_t</span> <span class="n">strtab_offset</span> <span class="o">=</span> <span class="n">symtab</span><span class="o">[</span><span class="n">symtab_index</span><span class="o">].</span><span class="n">n_un</span><span class="o">.</span><span class="n">n_strx</span><span class="p">;</span>
        <span class="sr">//</span> <span class="err">获取符号名</span>
        <span class="n">char</span> <span class="o">*</span><span class="n">symbol_name</span> <span class="o">=</span> <span class="n">strtab</span> <span class="o">+</span> <span class="n">strtab_offset</span><span class="p">;</span>
        <span class="sr">//</span> <span class="err">过滤掉符号名小于</span> <span class="mi">4</span> <span class="err">位的符号</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">strnlen</span><span class="p">(</span><span class="n">symbol_name</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">continue</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="sr">//</span> <span class="err">取出</span> <span class="n">rebindings</span> <span class="err">结构体实例数组，开始遍历链表</span>
        <span class="n">struct</span> <span class="n">rebindings_entry</span> <span class="o">*</span><span class="n">cur</span> <span class="o">=</span> <span class="n">rebindings</span><span class="p">;</span>
        <span class="k">while</span> <span class="p">(</span><span class="n">cur</span><span class="p">)</span> <span class="p">{</span>
            <span class="sr">//</span> <span class="err">对于链表中每一个</span> <span class="n">rebindings</span> <span class="err">数组的每一个</span> <span class="n">rebinding</span> <span class="err">实例</span>
            <span class="sr">//</span> <span class="err">依次在</span> <span class="nb">String</span> <span class="no">Table</span> <span class="err">匹配符号名</span>
            <span class="k">for</span> <span class="p">(</span><span class="n">uint</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="n">cur</span><span class="o">-&gt;</span><span class="n">rebindings_nel</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
                <span class="sr">//</span> <span class="err">符号名与方法名匹配</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">strcmp</span><span class="p">(</span><span class="o">&amp;</span><span class="n">symbol_name</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span><span class="p">,</span> <span class="n">cur</span><span class="o">-&gt;</span><span class="n">rebindings</span><span class="o">[</span><span class="n">j</span><span class="o">].</span><span class="n">name</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
                    <span class="sr">//</span> <span class="err">如果是第一次对跳转地址进行重写</span>
                    <span class="k">if</span> <span class="p">(</span><span class="n">cur</span><span class="o">-&gt;</span><span class="n">rebindings</span><span class="o">[</span><span class="n">j</span><span class="o">].</span><span class="n">replaced</span> <span class="o">!=</span> <span class="no">NULL</span> <span class="o">&amp;&amp;</span>
                        <span class="n">indirect_symbol_bindings</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">!=</span> <span class="n">cur</span><span class="o">-&gt;</span><span class="n">rebindings</span><span class="o">[</span><span class="n">j</span><span class="o">].</span><span class="n">replacement</span><span class="p">)</span> <span class="p">{</span>
                        <span class="sr">//</span> <span class="err">记录原始跳转地址</span>
                        <span class="o">*</span><span class="p">(</span><span class="n">cur</span><span class="o">-&gt;</span><span class="n">rebindings</span><span class="o">[</span><span class="n">j</span><span class="o">].</span><span class="n">replaced</span><span class="p">)</span> <span class="o">=</span> <span class="n">indirect_symbol_bindings</span><span class="o">[</span><span class="n">i</span><span class="o">]</span><span class="p">;</span>
                    <span class="p">}</span>
                    <span class="sr">//</span> <span class="err">重写跳转地址</span>
                    <span class="n">indirect_symbol_bindings</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">=</span> <span class="n">cur</span><span class="o">-&gt;</span><span class="n">rebindings</span><span class="o">[</span><span class="n">j</span><span class="o">].</span><span class="n">replacement</span><span class="p">;</span>
                    <span class="sr">//</span> <span class="err">完成后不再对当前</span> <span class="no">Indirect</span> <span class="no">Symbol</span> <span class="err">处理</span>
                    <span class="sr">//</span> <span class="err">继续迭代到下一个</span> <span class="no">Indirect</span> <span class="no">Symbol</span>
                    <span class="sr">/** 加入调试代码 **/</span>
                    <span class="nb">printf</span><span class="p">(</span><span class="s2">&quot;</span><span class="se">\n\n</span><span class="s2">Symbol Name: %s</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">symbol_name</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span><span class="p">);</span>
                    <span class="nb">printf</span><span class="p">(</span><span class="s2">&quot;Rebinding Name: %s</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">,</span> <span class="n">cur</span><span class="o">-&gt;</span><span class="n">rebindings</span><span class="o">[</span><span class="n">j</span><span class="o">].</span><span class="n">name</span><span class="p">);</span>
                    <span class="nb">printf</span><span class="p">(</span><span class="s2">&quot;Origin Addr: 0x%X</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">,</span> <span class="n">cur</span><span class="o">-&gt;</span><span class="n">rebindings</span><span class="o">[</span><span class="n">j</span><span class="o">].</span><span class="n">replaced</span><span class="p">);</span>
                    <span class="nb">printf</span><span class="p">(</span><span class="s2">&quot;Rebinding Addr: 0x%X</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">,</span> <span class="n">cur</span><span class="o">-&gt;</span><span class="n">rebindings</span><span class="o">[</span><span class="n">j</span><span class="o">].</span><span class="n">replacement</span><span class="p">);</span>
                    <span class="sr">/** 调试代码 END **/</span>
                    <span class="n">goto</span> <span class="n">symbol_loop</span><span class="p">;</span>
                <span class="p">}</span>
            <span class="p">}</span>
            <span class="sr">//</span> <span class="err">链表遍历</span>
            <span class="n">cur</span> <span class="o">=</span> <span class="n">cur</span><span class="o">-&gt;</span><span class="k">next</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="ss">symbol_loop</span><span class="p">:;</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></div>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Symbol</span> <span class="ss">Name</span><span class="p">:</span> <span class="n">strlen</span>
<span class="no">Rebinding</span> <span class="ss">Name</span><span class="p">:</span> <span class="n">strlen</span>
<span class="no">Origin</span> <span class="ss">Addr</span><span class="p">:</span> <span class="mh">0x20A0</span>
<span class="no">Rebinding</span> <span class="ss">Addr</span><span class="p">:</span> <span class="mh">0x1DA0</span>
<span class="mi">666</span>
<span class="no">Program</span> <span class="n">ended</span> <span class="n">with</span> <span class="nb">exit</span> <span class="ss">code</span><span class="p">:</span> <span class="mi">0</span></code></pre></div>
<p><code>0x1DA0</code> 这个地址在原始的间接表指向的位置之前，所以我们大致断定它处在 <code>_TEXT</code> 段。并且<code>__stub_helper</code>, <code>__cstring</code>, <code>__unwoind_info</code> 这些 Session 是我们无法直接干预的位置，所以可以猜测 <code>0x1DA0</code> 落在我们的 <code>__text</code> Session 中。我们掏出 Hopper 来验证一下这个猜想，发现正是如此：</p>
<p><img src="../assets/images/blog/15142784594980/15177186003911.jpg" alt="" /></p>
<h2 id="section-2">尾声</h2>
<p>fishhook 的原理探究至此就告一段落。通过这个源码探求，强化了对于 Mach-O 的学习和认识，也在之中学习到了 Facebook 对于 Hook C 方法这个很妙的技巧。最后不得不再惊叹一句，FB 真的是令技术人向往的地方。</p>
<h2 id="section-3">参考</h2>
<ul>
  <li><a href="http://turingh.github.io/2016/05/24/nlist-Mach-O%E6%96%87%E4%BB%B6%E9%87%8D%E5%AE%9A%E5%90%91%E4%BF%A1%E6%81%AF%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%88%86%E6%9E%90/">nlist-Mach-O文件重定向信息数据结构分析</a></li>
  <li><a href="http://blog.c0smic.cn/2017/04/18/aslr/">地址空间布局随机化(ASLR)机制的分析与绕过</a></li>
</ul>
  <footer class="post-footer">
    <section class="author">
      <h4>Desgard_Duan</h4>
      <p>I write many code to write less code.💻</p>
    </section>
<aside class="share">
  <h4>Share this.</h4>
  <a href="http://twitter.com/share?text=《验证试验 - 探求 fishhook 原理（二）》 -- Guardia · 瓜地&amp;url=https://www.desgard.com/fishhook-2/"
  onclick="window.open(this.href, 'twitter-share', 'width=550,height=235');return false;">
    <i class="fa fa-twitter-square"></i>
  </a>
  <a href="http://v.t.sina.com.cn/share/share.php?url=https://www.desgard.com/fishhook-2/&amp;title=《验证试验 - 探求 fishhook 原理（二）》 —— Guardia · 瓜地" onclick="window.open(this.href, 'twitter-share', 'width=550,height=235');return false;">
    <i class="fa fa-weibo"></i>
  </a>
</aside>
      <hr>
<aside id="comments" class="disqus">
  <div id="container"></div>
  <!-- <link rel="stylesheet" href="/assets/css/gitment.css">
  <script src="https://imsun.github.io/gitment/dist/gitment.browser.js"></script>
  <script>
  var gitment = new Gitment({
    id: "https://www.desgard.com/fishhook-2/", 
    owner: 'Desgard',
    repo: 'desgard.github.com',
    oauth: {
      client_id: 'e2612df42f3f2a83e71c',
      client_secret: 'b53e85b314bb24a6d06773e48bbb62a4de3b8b3a',
    },
  })
  gitment.render('container')
  </script> -->
<link rel="stylesheet" href="/assets/css/gitalk.css">
<script src="https://unpkg.com/gitalk/dist/gitalk.min.js"></script>
<div id='gitalk-container'></div>
<script>
    const gitalk = new Gitalk({
        id: "https://www.desgard.com/fishhook-2/", 
        clientID: 'e2612df42f3f2a83e71c',
        clientSecret: 'b53e85b314bb24a6d06773e48bbb62a4de3b8b3a',
        repo: 'desgard.github.com',
        owner: 'Desgard',
        admin: ['Desgard'],
        // facebook-like distraction free mode
        distractionFreeMode: false
    })
    gitalk.render('gitalk-container')
</script>
</aside>
  </footer>
</article>
    </main>
<footer class="footer">
  <div class="container">
    <ul class="icons">
      <li>
        <a href="https://github.com/desgard" class="icon-github" target="_blank">
          <i class="fa fa-github"></i>
        </a>
      </li>
      <li>
        <a href="https://www.facebook.com/desgard.duan" class="icon-facebook" target="_blank">
          <i class="fa fa-facebook"></i>
        </a>
      </li>
      <li>
        <a href="https://twitter.com/Desgard_Duan" class="icon-twitter" target="_blank">
          <i class="fa fa-twitter"></i>
        </a>
      </li>
      <li>
        <a href="https://stackoverflow.com/users/6119149/desgard-duan" class="icon-github" target="_blank">
          <i class="fa fa-stack-overflow"></i>
        </a>
      </li>
      <li>
        <a href="https://weibo.com/desgard" class="icon-instagram" target="_blank">
          <i class="fa fa-weibo"></i>
        </a>
      </li>
    </ul>
    <p>
      <q>I write many code to write less code.💻</q>
      <small>– Gua</small>
    </p>
    <small class="clearfix">
      Powered by <a href="http://jekyllrb.com" target="_blank">Jekyll</a> • <a href="https://github.com/desgard" target="_blank">Open source <i class="fa fa-heart"></i></a>
    </small>
  </div>
</footer>
<a class="scroll-up fa fa-chevron-up bounce" href="#" data-position="0"></a>
<div id="modalSearch" class="modal">
  <div class="modal__overlay"></div>
  <div class="modal__content">
    <a href="#!" class="modal-close" data-modal-close>&times;</a>
    <div class="search-container">
      <input type="text" id="search-input" placeholder="Search articles">
      <ul id="results-container"></ul>
    </div>
  </div>
</div>
    <script src="/assets/main-52d417e8a6ff9f5b168386d37c96338a.js"></script>
  </body>
  <script>
    var link = "" ;
    var os = function() {  
      var ua = navigator.userAgent,  
      isWindowsPhone = /(?:Windows Phone)/.test(ua),  
      isSymbian = /(?:SymbianOS)/.test(ua) || isWindowsPhone,   
      isAndroid = /(?:Android)/.test(ua),   
      isFireFox = /(?:Firefox)/.test(ua),   
      isChrome = /(?:Chrome|CriOS)/.test(ua),  
      isTablet = /(?:iPad|PlayBook)/.test(ua) || (isAndroid && !/(?:Mobile)/.test(ua)) || (isFireFox && /(?:Tablet)/.test(ua)),  
      isPhone = /(?:iPhone)/.test(ua) && !isTablet,  
      isPc = !isPhone && !isAndroid && !isSymbian;  
      return {  
        isTablet: isTablet,  
        isPhone: isPhone,  
        isAndroid : isAndroid,  
        isPc : isPc  
      };  
    }();  
    if (link.length > 0) {
      if (os.isAndroid || os.isPhone) {
        location.replace(link);
      }
    }
  </script>
</html>